/********************************************************************
Created:	2012/04/03  8:19
Filename: 	RLib_Compress.cpp
Author:		rrrfff
Url:	    http://blog.csdn.net/rrrfff
Used:       zlib 1.2.6
 *********************************************************************/
#include "RLib_Compress.h"
using namespace System::IO;
//////////////////////////////////////////////////////////////////////////
#include "support/zlib/zlib.h"
#include "support/zlib/zutil.h"

//-------------------------------------------------------------------------

extern "C" voidpf ZLIB_INTERNAL my_zcalloc (voidpf/* opaque*/, unsigned items, unsigned size)
{
	return (voidpf)RLIB_GlobalAlloc(items * size);
}

//-------------------------------------------------------------------------

extern "C" void ZLIB_INTERNAL my_zcfree (voidpf/* opaque*/, voidpf ptr)
{
	RLIB_GlobalCollect(ptr);
}

//-------------------------------------------------------------------------

UnGZipStream *GZip::UnGzip(const Stream *src, long bytes /* = 0 */, int windowBits /* = -MAX_WBITS */)
{
	bytes = bytes > 0 ? bytes : src->MaxReadSize;
	if (bytes <= 0)
	{
		return nullptr;
	} //if

	//output
	UnGZipStream *pUnGZipStream = new UnGZipStream(bytes * 4/*??*/);
	if (pUnGZipStream == nullptr)
	{
		return nullptr;
	} //if

	//input
	Bytef *zdata = LPBYTE(src->CurrentPtr);
	uLong nzdata = bytes;

	z_stream tz_stream = {0}; /* decompression stream */
	tz_stream.zalloc   = my_zcalloc;
	tz_stream.zfree    = my_zcfree;
	if(inflateInit2(&tz_stream, windowBits) != Z_OK)
	{
		assert(!"inflateInit2 Error!");
		goto ERROR_RETURN;
	}
	while (nzdata > tz_stream.total_in) 
	{
		if (tz_stream.avail_in == 0)
		{
			tz_stream.avail_in = nzdata - tz_stream.total_in;
			tz_stream.next_in  = zdata + tz_stream.total_in;
		} //if

		if (tz_stream.avail_out == 0)
		{
			bytes = pUnGZipStream->Size - tz_stream.total_out;
			assert(bytes >= 0);
			if (bytes == 0)
			{
				if(!pUnGZipStream->AppendSize(tz_stream.avail_in * 4/*??*/))
				{
					assert(!"LACK OF MEMORY");
					goto CASE_Z_ERROR;
				}
				bytes = pUnGZipStream->Size - tz_stream.total_out;
				assert(bytes >= 0);
			} //if
			tz_stream.avail_out = bytes; 
			tz_stream.next_out  = (Bytef *)pUnGZipStream->ObjectData + tz_stream.total_out;
		} //if

		switch (inflate(&tz_stream, Z_NO_FLUSH))
		{
		case Z_OK:
			continue;
		case Z_STREAM_END:
			goto CASE_Z_STREAM_END;
		case Z_DATA_ERROR:
			//assert(!"Z_DATA_ERROR");
			goto CASE_Z_ERROR;
		default:
			assert(!"Z_ERROR");
			goto CASE_Z_ERROR;
		}
	}

	goto CASE_Z_STREAM_END;

CASE_Z_ERROR:
	if(inflateEnd(&tz_stream) != Z_OK)
	{
		assert(!"inflateEnd Error!");
	}
	if(tz_stream.total_out > 0) goto CASE_Z_ERROR_HAS_OUTPUT;
	goto ERROR_RETURN;

CASE_Z_STREAM_END:
	if(inflateEnd(&tz_stream) != Z_OK)
	{
		assert(!"inflateEnd Error!");
		goto ERROR_RETURN;
	}

CASE_Z_ERROR_HAS_OUTPUT:
	pUnGZipStream->Position = 0;
	pUnGZipStream->Length   = tz_stream.total_out;
	return pUnGZipStream;

ERROR_RETURN:
	delete pUnGZipStream;
	return nullptr;
}

//-------------------------------------------------------------------------

GZipStream *GZip::Gzip(const Stream *src, long bytes /* = 0 */, int windowBits /* = -MAX_WBITS */)
{
	bytes = bytes > 0 ? bytes : src->MaxReadSize;
	if (bytes <= 0)
	{
		return nullptr;
	} //if

	//output
	UnGZipStream *pGZipStream = new UnGZipStream(bytes);
	if (pGZipStream == nullptr)
	{
		return nullptr;
	} //if

	//input
	Byte *zdata  = LPBYTE(src->CurrentPtr);
	uLong nzdata = bytes;

	z_stream tz_stream = {0}; /* decompression stream */
	tz_stream.zalloc   = my_zcalloc;
	tz_stream.zfree    = my_zcfree;
	if(deflateInit2(&tz_stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, windowBits, 8, Z_DEFAULT_STRATEGY) != Z_OK)
	{
		assert(!"deflateInit2 Error!");
		goto ERROR_RETURN;
	}
	while (nzdata > tz_stream.total_in) 
	{
		if (tz_stream.avail_in == 0)
		{
			tz_stream.avail_in = nzdata - tz_stream.total_in;
			tz_stream.next_in  = zdata + tz_stream.total_in;
		} //if

		if (tz_stream.avail_out == 0)
		{
			bytes = pGZipStream->Size - tz_stream.total_out;
			assert(bytes >= 0);
			if (bytes == 0)
			{
				if(!pGZipStream->AppendSize(tz_stream.avail_in * 4))
				{
					assert(!"LACK OF MEMORY");
					goto CASE_Z_ERROR;
				}
				bytes = pGZipStream->Size - tz_stream.total_out;
				assert(bytes >= 0);
			} //if
			tz_stream.avail_out = bytes; 
			tz_stream.next_out  = (Bytef *)pGZipStream->ObjectData + tz_stream.total_out;
		} //if

		switch (deflate(&tz_stream, Z_NO_FLUSH))
		{
		case Z_OK:
			continue;
		case Z_STREAM_END:
			goto CASE_Z_STREAM_END;
		case Z_DATA_ERROR:
			//assert(!"Z_DATA_ERROR");
			goto CASE_Z_ERROR;
		default:
			assert(!"Z_ERROR");
			goto CASE_Z_ERROR;
		}
	}

	goto CASE_Z_STREAM_END;

CASE_Z_ERROR:
	if(deflateEnd(&tz_stream) != Z_OK)
	{
		assert(!"deflateEnd Error!");
	}
	if(tz_stream.total_out > 0) goto CASE_Z_ERROR_HAS_OUTPUT;
	goto ERROR_RETURN;

CASE_Z_STREAM_END:
	if(deflateEnd(&tz_stream) != Z_OK)
	{
		assert(!"deflateEnd Error!");
		goto ERROR_RETURN;
	}

CASE_Z_ERROR_HAS_OUTPUT:
	pGZipStream->Position = 0;
	pGZipStream->Length   = tz_stream.total_out;
	return pGZipStream;

ERROR_RETURN:
	delete pGZipStream;
	return nullptr;
}